DYNAMO : Purge URL et Omniclass dans Familles .rfa avec script python
Etapes 01 : Diagnostic
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
# Mots-clés à détecter (insensible à la casse)
KEYWORDS = ["omniclass", "url", "http", "www",
"manufacturer", "model", "assembly",
"keynote", "description", "reference"]
def has_keyword(text):
t = text.lower()
return any(k in t for k in KEYWORDS)
def get_params(element):
found = []
for p in element.Parameters:
try:
if p.StorageType == StorageType.String:
val = p.AsString()
if val and val.strip() != "":
name = p.Definition.Name
# Garde si nom OU valeur contient un mot-clé
if has_keyword(name) or has_keyword(val):
found.append((name, val))
except:
pass
return found
results = []
# Scan Family (niveau famille)
fam_collector = FilteredElementCollector(doc).OfClass(Family)
for fam in fam_collector:
cat = fam.FamilyCategory.Name if fam.FamilyCategory else "—"
params = get_params(fam)
for (pname, pval) in params:
results.append([
fam.Name, "— (Family)", cat, pname, pval
])
# Scan FamilySymbol (niveau type)
sym_collector = FilteredElementCollector(doc).OfClass(FamilySymbol)
for fs in sym_collector:
try:
fam = fs.Family
cat = fam.FamilyCategory.Name if fam.FamilyCategory else "—"
params = get_params(fs)
for (pname, pval) in params:
results.append([
fam.Name, fs.Name, cat, pname, pval
])
except:
pass
if results:
OUT = [["Famille", "Type", "Catégorie", "Paramètre", "Valeur"]] + results
else:
OUT = [["Aucun paramètre suspect trouvé"]]
Etapes 03 : Purge
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
import os
import tempfile
doc = DocumentManager.Instance.CurrentDBDocument
# Fermer toute transaction ouverte
try:
TransactionManager.Instance.ForceCloseTransaction()
except:
pass
TARGET_FAMILIES = [
"AEX_CAN_RSX_MANCHON_ELECTRO",
"AEX_CAN_RSX_MANCHON_THERMIQUE",
"AEX_CAN_RSX_CAPE_DE_FERMETURE_GEBERIT",
"AEX_ELE_RX_COUDE_RACCORD",
"AEX_CAN_RSX_M_ELBOW_GENERIC",
"AEX_CAN_RSX_M_CROSS_GENERIC",
"AEX_CAN_RSX_M_COUPLING_GENERIC",
"AEX_CAN_RSX_M_TEE_GENERIC",
"AEX_CAN_RSX_M_TRANSITION_GENERIC",
"AEX_CAN_RSX_FERMETURE_ATTENTE",
"AEX_SAN_POMPE",
"AEX_SAN_VASQUE",
"AEX_PLA_HAIE",
"AEX_PLA_HAIE_01",
"AEX_PLA_HAIE_03",
"AEX_PLA_ARBUSTE_16",
"AEX_PLANT_Label",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_01",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_02",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_03",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_04",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_05",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_06",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_07",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_08",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_10",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_11",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_12",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_14",
"AEX_PLANT_S_TOUS_Bushy02_ARBUSTE_15",
"AEX_PLANT_S_TOUS_Bushy02_CEPEE_FEUILLU_PROJETE",
"AEX_PLANT_T_TOUS_CONICAL02_ARBRE_CONIFERE_PROJETE",
"AEX_PLANT_T_TOUS_CONICAL02_ARBRE_FEUILLU_PROJETE",
"AEX_PLANT_T_TOUS_CONICAL02_ARBRE_FEUILLU_TRANSPLANTE",
"AEX_PLANT_T_TOUS_CONICAL02_ARBUSTE_13",
"AEX_PLANT_T_TOUS_CONICAL02_CEPEE_FEUILLU_PROJETE",
"AEX_PLA_TIGE_01",
"AEX_GDC_BARREAU_25MM",
"AEX_GDC_BARREAU_190MM"
]
PARAMS_TO_PURGE = ["OmniClass Number", "OmniClass Title"]
tmp_dir = tempfile.gettempdir()
done = []
failed = []
fam_collector = list(FilteredElementCollector(doc).OfClass(Family))
for fam in fam_collector:
if fam.Name not in TARGET_FAMILIES:
continue
fam_doc = None
t = None
t_reload = None
try:
# 1. Ouvrir la famille
fam_doc = doc.EditFamily(fam)
# 2. Modifier dans le doc famille
t = Transaction(fam_doc, "Purge OmniClass")
t.Start()
for fs in FilteredElementCollector(fam_doc).OfClass(FamilySymbol):
for pname in PARAMS_TO_PURGE:
p = fs.LookupParameter(pname)
if p and not p.IsReadOnly:
p.Set("")
p1 = fs.get_Parameter(BuiltInParameter.OMNICLASS_CODE)
p2 = fs.get_Parameter(BuiltInParameter.OMNICLASS_DESCRIPTION)
if p1 and not p1.IsReadOnly: p1.Set("")
if p2 and not p2.IsReadOnly: p2.Set("")
t.Commit()
# 3. Sauvegarder en .rfa temporaire
rfa_path = os.path.join(tmp_dir, fam.Name + ".rfa")
save_opt = SaveAsOptions()
save_opt.OverwriteExistingFile = True
fam_doc.SaveAs(rfa_path, save_opt)
fam_doc.Close(False)
fam_doc = None
# 4. Recharger dans le projet — pas de Family() vide
t_reload = Transaction(doc, "Reload " + fam.Name)
t_reload.Start()
doc.LoadFamily(rfa_path)
t_reload.Commit()
done.append(fam.Name)
except Exception as e:
try:
if t and t.HasStarted() and not t.HasEnded():
t.RollBack()
except: pass
try:
if t_reload and t_reload.HasStarted() and not t_reload.HasEnded():
t_reload.RollBack()
except: pass
try:
if fam_doc:
fam_doc.Close(False)
except: pass
failed.append(fam.Name + " — " + str(e))
OUT = ["=== TRAITÉS (" + str(len(done)) + ") ==="] + done + \
["", "=== ÉCHECS (" + str(len(failed)) + ") ==="] + failed
NOTE : Workflow complet en 3 étapes :
Lance le diagnostic → note les familles avec OmniClass/URL
Copie ces noms dans TARGET_FAMILIES du script 2
Lance le script 2→ vérifie 0 échecs → relance le diagnostic pour confirmer → sauvegarde Revit
